﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;
using System.Text.RegularExpressions;
using System.Net;
using System.Collections.Specialized;
using System.Xml.Linq;
using System.Security.Cryptography;
using System.Xml;
using System.Threading;
using System.Globalization;
using Newtonsoft.Json;

namespace AutoEncodeDaemon
{
    class ReleaseEnvironment
    {
        public Dictionary<string, string> Infos;
        private string pathMediaInfo;
        private string pathffmpeg;
        private string pathmktorrent;
        private XDocument LoadFromStream(Stream stream)
        {
            using (XmlReader reader = XmlReader.Create(stream))
            {
                return XDocument.Load(reader);
            }
        }
        private string GetImgImgur(string file, int time)
        {
            XDocument x;
            NameValueCollection values;
            byte[] response;
            string imgpath = Path.Combine(Properties.Settings.Default.TempPath, Infos["RELEASENAME"] + "." + time + ".img.jpg");
            exeAndGetOutput(pathffmpeg, "-vframes 1 -an -ss " + time + " -i \"" + file + "\" \"" + imgpath +"\"");
            values = new NameValueCollection
            {
                { "key", Properties.Settings.Default.ImgurApiKey},
                { "image", Convert.ToBase64String(File.ReadAllBytes(imgpath)) }
            };
            for (; ; Thread.Sleep(30000))
            {
                try
                {
                    WebClient w = new WebClient();
                    if (String.IsNullOrEmpty(Properties.Settings.Default.ProxyServer) == false) w.Proxy = new WebProxy(Properties.Settings.Default.ProxyServer, Properties.Settings.Default.ProxyPort);
                    response = w.UploadValues("http://imgur.com/api/upload.xml", values);
                    x = LoadFromStream(new MemoryStream(response));
                    if (x.Element("rsp") == null || x.Element("rsp").HasAttributes == false || x.Element("rsp").FirstAttribute.Value != "ok") continue;
                }
                catch (Exception ex)
                {
                    AutoEncodeLog.Log2File(String.Format("Upload screenshot to Imgur encountered an error: {0} ", ex.ToString()));
                    continue;
                }
                File.Delete(imgpath);
                AutoEncodeLog.Log2File(String.Format("Image \"{0}\" uploaded ", x.Element("rsp").Element("original_image").Value));
                return "[url=" + x.Element("rsp").Element("original_image").Value + "][img]" + x.Element("rsp").Element("large_thumbnail").Value.Replace("l.jpg", "m.jpg") + "[/img][/url]";
            }
        }
        private string GetImgImagebam(string file, int time)
        {
            XDocument x;
            NameValueCollection values;
            string imgpath = Path.Combine(Properties.Settings.Default.TempPath, Infos["RELEASENAME"] + "." + time + ".img.jpg");
            exeAndGetOutput(pathffmpeg, "-vframes 1 -an -ss " + time + " -i \"" + file + "\" \"" + imgpath + "\"");
            values = new NameValueCollection
            {
                { "oauth_consumer_key", Properties.Settings.Default.ImagebamOauthApiKey},
                { "oauth_token", Properties.Settings.Default.ImagebamOauthToken},
                { "oauth_signature_method", "MD5" },
                { "oauth_signature", "" },
                { "oauth_timestamp", "" },
                { "oauth_nonce", "" },
                { "content_type", "family"},
                { "thumb_format", "JPG"},
                { "thumb_size", "180x180"},
                { "thumb_cropping", "0"},
            };
            for (; ; Thread.Sleep(30000))
            {
                values["oauth_timestamp"] = (DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1, 0, 0, 0, 0))).TotalSeconds.ToString();
                values["oauth_nonce"] = (DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1, 0, 0, 0, 0))).TotalSeconds.ToString();
                values["oauth_signature"] = BitConverter.ToString(MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(values["oauth_consumer_key"] + Properties.Settings.Default.ImagebamOauthApiSecret + values["oauth_timestamp"] + values["oauth_nonce"] + values["oauth_token"] + Properties.Settings.Default.ImagebamOauthTokenSecret))).Replace("-", string.Empty).ToLower();
                HttpWebResponse wresp=null;
                try
                {
                    HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create("http://www.imagebam.com/sys/API/resource/upload_image");
                    string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
                    if (String.IsNullOrEmpty(Properties.Settings.Default.ProxyServer) == false) request.Proxy = new WebProxy(Properties.Settings.Default.ProxyServer, Properties.Settings.Default.ProxyPort);
                    request.Method = "POST";
                    request.AllowAutoRedirect = false;
                    request.ContentType = "multipart/form-data; boundary=" + boundary;
                    Stream rs = request.GetRequestStream();
                    byte[] boundarybytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");
                    foreach (string key in values.Keys)
                    {
                        rs.Write(boundarybytes, 0, boundarybytes.Length);
                        byte[] formitembytes = System.Text.Encoding.UTF8.GetBytes(string.Format("Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}", key, values[key]));
                        rs.Write(formitembytes, 0, formitembytes.Length);
                    }
                    rs.Write(boundarybytes, 0, boundarybytes.Length);
                    byte[] headerbytes = System.Text.Encoding.UTF8.GetBytes("Content-Disposition: form-data; name=\"image\"; filename=\"img.jpg\"\r\nContent-Type: application/octet-stream\r\n\r\n");
                    rs.Write(headerbytes, 0, headerbytes.Length);
                    byte[] filecontent = File.ReadAllBytes(imgpath);
                    rs.Write(filecontent, 0, filecontent.Length);
                    byte[] trailer = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n");
                    rs.Write(trailer, 0, trailer.Length);
                    rs.Close();
                    wresp = (HttpWebResponse)request.GetResponse();
                    x = XDocument.Parse(JsonConvert.DeserializeXmlNode(new StreamReader(wresp.GetResponseStream()).ReadToEnd()).OuterXml);
                    if (x.Element("rsp") == null || x.Element("rsp").Element("status").Value != "ok") continue;
                }
                catch (Exception ex)
                {
                    AutoEncodeLog.Log2File(String.Format("Upload screenshot to Imagebam encountered an error: {0} ", ex.ToString()));
                    continue;
                }
                File.Delete(imgpath);
                AutoEncodeLog.Log2File(String.Format("Image \"{0}\" uploaded ", x.Element("rsp").Element("image").Element("URL").Value));
                return "[url=" + x.Element("rsp").Element("image").Element("URL").Value + "][img]" + x.Element("rsp").Element("image").Element("thumbnail").Value + "[/img][/url]";
            }
        }
        private string GetImgs(string file, int runtime, int shots)
        {
            string result = "";
            for (int i = 0; i < shots; i++)
            {
                if (Properties.Settings.Default.UseImagebam) result += GetImgImagebam(file, runtime / (shots + 1) * (i + 1));
                else result += GetImgImgur(file, runtime / (shots + 1) * (i + 1));
                if (i % 2 == 1) result += "\r\n";
                else result += " ";
            }
            return result;
        }
        private static string GetBiggestFile(string dir)
        {
            string biggestFile;
            biggestFile = "";
            foreach (var file in Directory.GetFiles(dir))
            {
                FileInfo oldFileInfo, newFileInfo;
                if (biggestFile == "")
                {
                    biggestFile = file;
                    continue;
                }
                oldFileInfo = new FileInfo(biggestFile);
                newFileInfo = new FileInfo(file);
                if (newFileInfo.Length > oldFileInfo.Length) biggestFile = file;
            }
            return biggestFile;
        }
        public static string exeAndGetOutput(string exe, string arguments)
        {
            Process p = new Process();
            int LocalProcessorAffinity = 0, RemoteProcessorAffinity = 0;
            p.StartInfo.CreateNoWindow = true;
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.RedirectStandardOutput = true;
            if (exe.StartsWith("@local"))
            {
                p.StartInfo.FileName = exe.Split(new char[] { '|' })[3];
                p.StartInfo.Arguments = arguments;
                LocalProcessorAffinity = (1 << Convert.ToInt32(exe.Split(new char[] { '|' })[4])) - 1;
            }
            else if (exe[0] == '@')
            {
                string affinity = "";
                string format = "\\\\{0} -low {1} -u {2} -p {3} \"{4}\" {5}";
                RemoteProcessorAffinity = Convert.ToInt32(exe.Split(new char[] { '|' })[4]);
                if (RemoteProcessorAffinity != 0)
                {
                    affinity = "-a ";
                    for (int i = 1; i < RemoteProcessorAffinity; i++)
                    {
                        affinity += i.ToString() + ",";
                    }
                    affinity += RemoteProcessorAffinity.ToString();
                }
                p.StartInfo.FileName = Properties.Settings.Default.PsExec;
                p.StartInfo.Arguments = String.Format(format, exe.Split(new char[] { '|' })[0].Substring(1), affinity, exe.Split(new char[] { '|' })[1], exe.Split(new char[] { '|' })[2], exe.Split(new char[] { '|' })[3], arguments);
            }
            else
            {
                p.StartInfo.FileName = exe;
                p.StartInfo.Arguments = arguments;
            }
            p.Start();
            try
            {
                // Fuck with C#, if you try to put the next line before p.Start(), you will get "No process is associated with this object"
                // If you put it after p.Start(), you may get "Cannot process request because the process has exited"
                p.PriorityClass = ProcessPriorityClass.Idle;
                if (LocalProcessorAffinity != 0) p.ProcessorAffinity = new IntPtr(LocalProcessorAffinity);
            }
            catch
            {
            }
            return p.StandardOutput.ReadToEnd();
        }
        public static string exeAndGetError(string exe, string arguments)
        {
            Process p = new Process();
            p.StartInfo.CreateNoWindow = true;
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.RedirectStandardError = true;
            if (exe[0] == '@')
            {
                string format = "\\\\{0} -low -u {1} -p {2} \"{3}\" {4}";
                p.StartInfo.FileName = Properties.Settings.Default.PsExec;
                p.StartInfo.Arguments = String.Format(format, exe.Split(new char[] { '|' })[0].Substring(1), exe.Split(new char[] { '|' })[1], exe.Split(new char[] { '|' })[2], exe.Split(new char[] { '|' })[3], arguments);
            }
            else
            {
                p.StartInfo.FileName = exe;
                p.StartInfo.Arguments = arguments;
            }
            p.Start();
            try
            {
                // Fuck with C#, if you try to put the next line before p.Start(), you will get "No process is associated with this object"
                // If you put it after p.Start(), you may get "Cannot process request because the process has exited"
                p.PriorityClass = ProcessPriorityClass.Idle;
            }
            catch
            {
            }
            return p.StandardError.ReadToEnd();
        }
        public static string GetReleaseFiles(string fileOrDir, bool targetIsDir)
        {
            if (targetIsDir) return fileOrDir.Split(new char[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries).Last().Replace(' ', '.');
            else return Path.GetFileName(fileOrDir).Replace(' ', '.');
        }
        public static string GetReleaseFiles(string fileOrDir)
        {
            return GetReleaseFiles(fileOrDir, Directory.Exists(fileOrDir));
        }
        public static string GetReleaseName(string fileOrDir, bool targetIsDir)
        {
            if (targetIsDir) return GetReleaseFiles(fileOrDir);
            else return Path.GetFileNameWithoutExtension(fileOrDir).Replace(' ', '.');
        }
        public static string GetReleaseName(string fileOrDir)
        {
            return GetReleaseName(fileOrDir,Directory.Exists(fileOrDir));
        }
        public ReleaseEnvironment(string fileOrDir, int shots)
        {
            string targetFile;
            bool targetIsDir;
            Infos = new Dictionary<string, string>();
            pathMediaInfo = Properties.Settings.Default.MediaInfoPath;
            pathffmpeg = Properties.Settings.Default.ffmpegPath;
            pathmktorrent = Properties.Settings.Default.mktorrentPath;
            targetIsDir = Directory.Exists(fileOrDir);

            if (targetIsDir) targetFile = GetBiggestFile(fileOrDir);
            else targetFile = fileOrDir;

            Infos["RELEASEFILES"] = GetReleaseFiles(fileOrDir, targetIsDir);

            Infos["RELEASENAME"] = GetReleaseName(fileOrDir, targetIsDir);

            Infos["RELEASECATALOG"] = "N/A";

            Infos["DATE"] = DateTime.Parse(exeAndGetOutput(pathMediaInfo, "--Inform=General;%Encoded_Date% \"" + targetFile + "\"").Replace("UTC ", "")).ToString("d", DateTimeFormatInfo.InvariantInfo);

            Infos["URL"] = "N/A";

            Infos["SOURCETYPE"] = "N/A";

            Infos["VCODECNAME"] = exeAndGetOutput(pathMediaInfo, "--Inform=Video;%Encoded_Library/Name% \"" + targetFile + "\"");

            Infos["VCODECREV"] = Regex.Match(exeAndGetOutput(pathMediaInfo, "--Inform=Video;%Encoded_Library/Version% \"" + targetFile + "\""), @"r\d+").Value;

            Infos["VCODECPROFILE"] = exeAndGetOutput(pathMediaInfo, "--Inform=Video;%Format_Profile% \"" + targetFile + "\"").Replace('@', ' ');

            Infos["VBITRATE"] = (Convert.ToInt32(exeAndGetOutput(pathMediaInfo, "--Inform=Video;%BitRate% \"" + targetFile + "\"")) / 1000).ToString() + " Kbps";

            Infos["FPS"] = exeAndGetOutput(pathMediaInfo, "--Inform=Video;%FrameRate% \"" + targetFile + "\"") + " fps";

            Infos["ALANGUAGE"] = "N/A";

            Infos["AUDIOTYPE"] = exeAndGetOutput(pathMediaInfo, "--Inform=Audio;%Codec%, \"" + targetFile + "\"").Split(new char[] { ',' })[0] + " " + exeAndGetOutput(pathMediaInfo, "--Inform=Audio;%ChannelPositions/String2%, \"" + targetFile + "\"").Split(new char[] { ',' })[0];

            Infos["ABITRATE"] = (Convert.ToInt32(exeAndGetOutput(pathMediaInfo, "--Inform=Audio;%BitRate%, \"" + targetFile + "\"").Split(new char[] {','})[0]) / 1000).ToString() + " Kbps";

            Infos["RUNTIME"] = exeAndGetOutput(pathMediaInfo, "--Inform=General;%Duration/String% \"" + targetFile + "\"");

            Infos["ASPECT"] = exeAndGetOutput(pathMediaInfo, "--Inform=Video;%DisplayAspectRatio/String% \"" + targetFile + "\"");

            Infos["RESOLUTION"] = exeAndGetOutput(pathMediaInfo, "--Inform=Video;%Width% \"" + targetFile + "\"") + "x" + exeAndGetOutput(pathMediaInfo, "--Inform=Video;%Height% \"" + targetFile + "\"");

            Infos["SUB"] = "N/A";

            Infos["FILESIZE"] = ((double)(new FileInfo(targetFile).Length) / 1024 / 1024 / 1024).ToString("F") + " GB";

            Infos["AUTHOR"] = "N/A";

            Infos["DESCRIPTION"] = "";

            Infos["LANGUAGE"] = "N/A";

            Infos["FILES"] = fileOrDir;

            Infos["POSTER"] = "";

            Infos["CASTIMG"] = "";

            Infos["SCREENSHOTS"] = GetImgs(targetFile, Convert.ToInt32(exeAndGetOutput(pathMediaInfo, "--Inform=General;%Duration% \"" + targetFile + "\"")) / 1000, shots);
        }
        public string Parse(string data)
        {
            foreach (var info in Infos)
            {
                data = data.Replace("$" + info.Key, info.Value);
            }
            return data;
        }
        public string MakeTorrent()
        {
            string result;
            Infos["TORRENTFILE"] = Path.Combine(Properties.Settings.Default.TempPath, Infos["RELEASENAME"] + ".torrent");
            File.Delete(Infos["TORRENTFILE"]);
            result = exeAndGetOutput(pathmktorrent, "-a http://tracker.chdbits.org/announce.php -c \"" + Infos["AUTHOR"] + "\" -l 21 -p -n \"" + Infos["RELEASEFILES"] + "\" -o \"" + Infos["RELEASENAME"] + ".torrent" + "\" \"" + Infos["FILES"] + "\"");
            if (File.Exists(Infos["RELEASENAME"] + ".torrent"))
            {
                File.Move(Infos["RELEASENAME"] + ".torrent", Infos["TORRENTFILE"]);
                AutoEncodeLog.Log2File(String.Format("MakeTorrent has successfully created torrent for \"{0}\"", Infos["FILES"]));
            }
            else
            {
                Infos["TORRENTFILE"] = "";
                AutoEncodeLog.Log2File(String.Format("MakeTorrent failed to created torrent for \"{0}\". MakeTorrent output: {1}", Infos["FILES"], result));
            }
            return result;
        }
    }
}
